﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Sharp.Interprocess
{
    public class SharedObservableCollection<T> : SharedCollection<T>, ICollectionChangedNotify
    {
        #region Private Members
        private Thread m_CollectionChangedObserver;
        private const int CHANGES_CHECK_LATENCY_MILLISECONDS = 100;
        private volatile bool m_IsRunning;
        private ulong m_CachedCollectionChangeCount;
        #endregion

        #region Constructor(s)
        public SharedObservableCollection(string collectionMapName, int collectionOffset, bool shouldAutoExpand = true, int collectionMaxSize = PAGED_MEMORY_FILE_DEFAULT_SIZE)
            :this(collectionMapName,collectionOffset,EqualityComparer<T>.Default,shouldAutoExpand,collectionMaxSize)
        {
        }

        public SharedObservableCollection(string collectionMapName, int collectionOffset, IEqualityComparer<T> equalityComparer,bool shouldAutoExpand = true, int collectionMaxSize = PAGED_MEMORY_FILE_DEFAULT_SIZE)
            : base(collectionMapName, collectionOffset, equalityComparer,shouldAutoExpand, collectionMaxSize)
        {
            m_IsRunning = true;
            m_CollectionChangedObserver = new Thread(new ThreadStart(CollectionChangedObserver_Thread));
            m_CollectionChangedObserver.IsBackground = true;
            m_CollectionChangedObserver.Name = "SharedObservableCollection Changes Observer " + Guid.NewGuid().ToString();

            m_CachedCollectionChangeCount = m_CollectionHeader.CollectionChangeCount;
            
            m_CollectionChangedObserver.Start();
        }
        #endregion

        public event Action CollectionChanged;

        public int ChangeDetectionLatency
        {
            get
            {
                return CHANGES_CHECK_LATENCY_MILLISECONDS;
            }
        }

        #region Thread Methods
        protected void CollectionChangedObserver_Thread()
        {
            while (m_IsRunning)
            {
                try
                {
                    m_CollectionCountMutex.WaitOne();
                    if (m_CollectionHeader.CollectionChangeCount != m_CachedCollectionChangeCount)
                    {
                        m_CachedCollectionChangeCount = m_CollectionHeader.CollectionChangeCount;
                        if (CollectionChanged != null)
                        {
                            CollectionChanged();
                        }
                    }
                }
                finally
                {
                    m_CollectionCountMutex.ReleaseMutex();
                }
                Thread.Sleep(CHANGES_CHECK_LATENCY_MILLISECONDS);
            }
        }
        #endregion

        #region IDisposable Implementation
        public override void Dispose()
        {
            m_IsRunning = false;
            m_CollectionChangedObserver.Join(CHANGES_CHECK_LATENCY_MILLISECONDS * 2);
            if (m_CollectionChangedObserver.IsAlive)
            {
                try
                {
                    m_CollectionChangedObserver.Abort();
                }
                catch { }
            }

            base.Dispose();
        }
        #endregion
    }
}
